﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Flattiverse;
using System.Threading;

namespace FlattiverseGame
{
    public partial class GameField : UserControl
    {
        Connector connector;
        UniverseGroup currentUniverse;
        Team currentTeam;

        Bitmap canvas;

        Thread serverCommitWorker;

        List<BaseShip> shipList = new List<BaseShip>();

        //Map map;

        private volatile bool isRunning = true;

        bool isInit = false;

        public bool IsInit
        {
            get { return isInit; }
        }

        float scale = 3f;

        public const float BarHeight = 15f;
        public const float BarDistance = 5f;

        #region Ctors
        public GameField()
        {
            InitializeComponent();
        }

        public GameField(Connector connector)
        {
            InitializeComponent();

            SetStyle(ControlStyles.UserPaint, true);
            SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
            SetStyle(ControlStyles.AllPaintingInWmPaint, true);

            this.connector = connector;

            MouseWheel += GameField_MouseWheel;

            serverCommitWorker = new Thread(MainCommitWorker);         
        }
        #endregion

        public void InitField(UniverseGroup universe, Team team, string userName)
        {
            currentUniverse = universe;
            currentTeam = team;

            currentUniverse.Join("dannyd", currentTeam);

            //shipList.Add(AddShip("Speedy Gonzales", "@Small"));
            //shipList.Add(AddShip("H3RP", "@Medium"));
            shipList.Add(AddShip("Fattlestar Balactica", "@Big"));

            //map = new Map("Who cares?");

            isInit = true;

            serverCommitWorker.Start();
        }

        public BaseShip AddShip(string name, string @class)
        {
            BaseShip ship = new BaseShip(currentUniverse, name);
            ship.CreateShip(@class);
            ship.Continue();

            return ship;
        }

        #region Main Worker
        private void MainCommitWorker()
        {
            while (isRunning)
            {
                if (currentUniverse != null)
                {
                    try
                    {
                        currentUniverse.Commit();

                        currentUniverse.PreWait();

                        currentUniverse.Wait();
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine(e.Message);
                    }
                }
                Thread.Sleep(1);
            }
        }
        #endregion

        public void Draw()
        {
            if (IsInit == false)
                throw new Exception("Nicht richtig initialisiert");

            if (canvas == null)
                canvas = new Bitmap(this.Size.Width, this.Size.Height);

            using (Graphics g = Graphics.FromImage(canvas))
            {
                g.Clear(Color.Black);

                int index = 0;
                
                foreach (BaseShip baseShip in shipList)
                {
                    Vector field = DivideField(shipList.Count, index++);

                    #region If Ship Alive
                    if (baseShip.IsAlive)
                    {
                        
                        baseShip.Draw(g, (int)field.X, (int)field.Y, scale);

                        #region Draw scanned Units
                        List<Unit> units = null;
                        if (baseShip.ScannedUnits != null)
                            units = new List<Unit>(baseShip.ScannedUnits);

                        if (units != null)
                        {
                            //map.TryAddNewUnits(units);

                            foreach (Unit u in units)
                            {

                                //if (u.Mobility == Mobility.Still)
                                //{
                                //    Vector position = new Vector(field.X + (u.Position.X) / scale, field.Y + (u.Position.Y) / scale);
                                //    map.DrawMapByRef(u.Name, g, position.X, position.Y , scale);
                                //    break;
                                //}
                                Vector position = new Vector(field.X + (u.Position.X - u.Radius) / scale, field.Y + (u.Position.Y - u.Radius) / scale);
                                Vector size = new Vector((u.Radius * 2) / scale, (u.Radius * 2) / scale);
                                Pen b = Pens.White;

                                #region Types
                                /*
                             * Unknown = -1,
                                //
                                // Summary:
                                //     A sun.
                                Sun = 1,
                                //
                                // Summary:
                                //     A planet.
                                Planet = 2,
                                //
                                // Summary:
                                //     A Moon.
                                Moon = 3,
                                //
                                // Summary:
                                //     A Meteoroid.
                                Meteoroid = 4,
                                //
                                // Summary:
                                //     A buoy.
                                Buoy = 5,
                                //
                                // Summary:
                                //     A wormhole.
                                WormHole = 33,
                                //
                                // Summary:
                                //     A mission target.
                                MissionTarget = 48,
                                //
                                // Summary:
                                //     A player ship.
                                PlayerShip = 64,
                                //
                                // Summary:
                                //     An explosion.
                                Explosion = 128,
                                //
                                // Summary:
                                //     A shot.
                                Shot = 129,
                                //
                                // Summary:
                                //     A space jellyfish.
                                SpaceJellyFish = 160,
                                //
                                // Summary:
                                //     A space jellyfish slime.
                                SpaceJellyFishSlime = 161,
                             */
                                #endregion
                                if (u is Sun)
                                {
                                    b = Pens.Yellow;

                                    foreach (Corona c in ((Sun)u).Coronas)
                                        g.DrawEllipse(Pens.Orange, field.X + (u.Position.X - c.Radius) / scale,
                                                                   field.Y + (u.Position.Y - c.Radius) / scale,
                                                                   (c.Radius * 2) / scale,
                                                                   (c.Radius * 2) / scale);
                                }
                                else if (u is Planet)
                                {
                                    b = Pens.DarkGreen;
                                }
                                #region PlayerUnit
                                else if (u is PlayerUnit)
                                {
                                    b = Pens.IndianRed;

                                    if (u is PlayerShip)
                                    {
                                        PlayerShip ps = (PlayerShip)u;

                                        // Draw Health
                                        float healthPercentage = ps.ControllableInfo.Hull / ps.ControllableInfo.HullMax;

                                        g.FillRectangle(Brushes.OrangeRed, field.X + ((u.Position.X - ps.Radius * 3f) / scale),
                                                                           field.Y + ((u.Position.Y - ps.Radius * 2f - GameField.BarHeight * 2f) / scale),
                                                                           (ps.Radius * 6f * healthPercentage) / scale,
                                                                           GameField.BarHeight / scale);

                                        // Draw Health
                                        float energyPercentage = ps.ControllableInfo.Shield / ps.ControllableInfo.ShieldMax;

                                        g.FillRectangle(Brushes.LightBlue, field.X + ((u.Position.X - ps.Radius * 3f) / scale),
                                                                           field.Y + ((u.Position.Y - ps.Radius * 2f - GameField.BarHeight) / scale),
                                                                           (ps.Radius * 6f * energyPercentage) / scale,
                                                                           GameField.BarHeight / scale);

                                        using (Font f = new Font("Tahoma", 10f, FontStyle.Bold))
                                        using (Brush teamColor = new SolidBrush(ps.Team.Color))
                                        {
                                            SizeF stringSize = g.MeasureString(ps.Team.Name, f);
                                            g.DrawString(ps.Team.Name, f, teamColor, position.X, position.Y + size.Y);
                                        }
                                    }
                                }
                                #endregion
                                else if (u is Moon)
                                    b = Pens.LightGray;
                                else if (u is Meteoroid)
                                    b = Pens.SandyBrown;
                                else if (u is Asteroid)
                                    b = Pens.LightSeaGreen;
                                else if (u is Shot)
                                {
                                    b = Pens.PaleVioletRed;

                                    size.X = 12f / scale;
                                    size.Y = 12f / scale;
                                }
                                else if (u is MissionTarget)
                                {
                                    b = Pens.Pink;

                                    size.X = 16f / scale;
                                    size.Y = 16f / scale;

                                    MissionTarget mt = (MissionTarget)u;

                                    

                                    foreach (Vector hint in mt.Hints)
                                    {
                                        Vector transformedHint = new Vector(field.X + (hint.X - u.Radius) / scale, field.Y + (hint.Y - u.Radius) / scale);

                                        g.DrawLine(Pens.GreenYellow, position.X, position.Y, position.X + hint.X, position.Y + hint.Y);
                                    }
                                }
                                else if (u is Explosion)
                                    b = Pens.MediumVioletRed;
                                else if (u is SpaceJellyFish)
                                    b = Pens.MediumSpringGreen;
                                else if (u is SpaceJellyFishSlime)
                                {
                                    b = Pens.Lime;

                                    size.X = 12f / scale;
                                    size.Y = 12f / scale;
                                }

                                g.DrawEllipse(b, position.X, position.Y, size.X, size.Y);

                                if (u is Shot || u is Explosion || u is Buoy)
                                    continue;

                                if (u is Sun || u is Planet)
                                    using (Font f = new Font("Tahoma", 10f, FontStyle.Bold))
                                        g.DrawString(u.Name, f, Brushes.White, field.X + u.Position.X / scale - 13f, field.Y + u.Position.Y / scale - 13f);

                                else if (u is MissionTarget)
                                    using (Font f = new Font("Tahoma", 10f, FontStyle.Bold))
                                        g.DrawString(u.Name + " " + ((MissionTarget)u).SequenceNumber, f, Brushes.White, position.X - 13f, position.Y - 13f);
                                else                              
                                    using (Font f = new Font("Tahoma", 10f, FontStyle.Bold))
                                        g.DrawString(u.Name, f, Brushes.White, position.X - 13f, position.Y - 13f);

                            }

                        }
                        #endregion
                    }
                    #endregion
                    else
                    {
                        using (Font diedFont = new Font("Tahoma", 28f, FontStyle.Bold))
                        using (Font continueFont = new Font("Tahoma", 23f, FontStyle.Bold))
                        {
                            SizeF stringSize = g.MeasureString("YOU DIED", diedFont);
                            g.DrawString("YOU DIED", diedFont, Brushes.Red, field.X - stringSize.Width / 2f, field.Y - stringSize.Height * 2f);

                            stringSize = g.MeasureString("PRESS \"C\" TO CONTINUE", continueFont);
                            g.DrawString("PRESS \"C\" TO CONTINUE", continueFont, Brushes.LightGoldenrodYellow, field.X - stringSize.Width / 2f, field.Y + stringSize.Height);
                        }

                    }
                }
            }

            this.Invalidate();
        }

        private Vector DivideField(int count, int index)
        {
            Vector pos = new Vector();
            int width, height;

            width = this.Size.Width / (2 * count);

            pos.X = width + width * (2 * index);

            if (count <= 2)
            {
                height = this.Size.Height / 2;

                pos.Y = height;
            }
            else if (count > 2 && count <= 8)
            {
                height = this.Size.Height / 4;

                pos.Y = height;
            }

            return pos;
        }

        public void Close()
        {
            //map.PrintMap();

            foreach (BaseShip ship in shipList)
                ship.RemoveShip();

            while (isRunning)
            {
                foreach (BaseShip ship in shipList)
                    if (ship.IsActive == true)
                    {
                        isRunning = true;
                        break;
                    }
                    else
                    {
                        isRunning = false;
                    }
            }

            while (serverCommitWorker.IsAlive)
                Thread.Sleep(1);

            currentUniverse.Part();
        }

        #region Events

        private void GameField_Paint(object sender, PaintEventArgs e)
        {
            if (canvas != null)
            {
                e.Graphics.DrawImage(canvas, 0, 0);
            }
        }

        private void GameField_KeyDown(object sender, KeyEventArgs e)
        {
            foreach (BaseShip ship in shipList)
            {
                if (e.KeyCode == Keys.C)
                    ship.Continue();
                else if (e.KeyCode == Keys.H)
                    ship.HoldShip();
                else if (e.KeyCode == Keys.Q)
                {
                    ship.LoadShield = !ship.LoadShield;
                    ship.RepairSelf = false;
                }
                else if (e.KeyCode == Keys.W)
                {
                    ship.RepairSelf = !ship.RepairSelf;
                    ship.LoadShield = false;
                }
            }
        }

        private void GameField_Resize(object sender, EventArgs e)
        {
            if (canvas != null && (this.Size.Width > 1 && this.Size.Height > 1))
            {
                canvas.Dispose();
                canvas = new Bitmap(this.Size.Width, this.Size.Height);
            }
        }

        private void GameField_MouseDown(object sender, MouseEventArgs e)
        {
            for (int i = 0; i < shipList.Count; i++)
            {
                Vector field = DivideField(shipList.Count, i);

                Vector clickVector = new Vector(e.X - field.X, e.Y - field.Y);

                if (e.Button == System.Windows.Forms.MouseButtons.Left)
                    shipList[i].MoveShip(clickVector, scale);
                else if (e.Button == System.Windows.Forms.MouseButtons.Right)
                    shipList[i].Shoot(clickVector, scale);
                
            }           
        }

        void GameField_MouseWheel(object sender, MouseEventArgs e)
        {
            if (e.Delta == 0)
                return;

            if (e.Delta > 0)
            {
                scale /= 1.2f;
            }
            else
            {
                scale *= 1.2f;
            }

            if (scale < 0.25f)
                scale = 0.25f;

            if (scale > 10f)
                scale = 10f;

            Draw();
        }
        #endregion

    }
}
